#include "net_include.h"
#include "common.h"
#include "recv_dbg.h"

#define SEND_BURST_SIZE 4
#define RECV_BURST_SIZE (SEND_BURST_SIZE * (numMachines))
#define WINDOW_SIZE 24

#define RECV_WAIT_TIME_SEC 0
#define RECV_WAIT_TIME_MICROSEC 2000
#define FINISH_WAIT_TIME_SEC 0
#define FINISH_WAIT_TIME_MICROSEC 10000
#define NACK_TIMEOUT 50      /* In milliseconds */

#define MAX_WAIT_TIMES ( (FINISH_WAIT_TIME_SEC*1000 + FINISH_WAIT_TIME_MICROSEC) / (RECV_WAIT_TIME_SEC*1000 + RECV_WAIT_TIME_MICROSEC) )

#define TYPE_MCAST_PACKET 2
#define TYPE_WINDOW_BLOCKED 3
#define TYPE_PACKET_EXHAUSTED 4

#define NAME_LENGTH 80
#define INFINITY 9999999

#define TRUE 1
#define FALSE 0

int gethostname(char*,size_t);

int getMyIP()
{
    struct hostent        h_ent;
    struct hostent        *p_h_ent;
    char                  my_name[NAME_LENGTH] = {'\0'};
    int                   my_ip;

    gethostname(my_name, NAME_LENGTH );
    printf("My host name is %s.\n", my_name);

    p_h_ent = gethostbyname(my_name);
    if ( p_h_ent == NULL ) {
        printf("myip: gethostbyname error.\n");
        exit(1);
    }

    memcpy( &h_ent, p_h_ent, sizeof(h_ent));
    memcpy( &my_ip, h_ent.h_addr_list[0], sizeof(my_ip) );

    printf("My IP address is: %d.%d.%d.%d\n", (htonl(my_ip) & 0xff000000)>>24,
					      (htonl(my_ip) & 0x00ff0000)>>16,
					      (htonl(my_ip) & 0x0000ff00)>>8,
					      (htonl(my_ip) & 0x000000ff) );
    printf("My IP=%d",my_ip);

    return my_ip;
}


struct WindowElement {
    char sendPkt[MAX_PACKET_SIZE];
    int isSpaceUsed;
};
typedef struct WindowElement WindowElement;

struct BufferElement {
    Header packetHdr;
    int isSpaceUsed;
    struct timeval lastNackSentTime;
};
typedef struct BufferElement BufferElement;

struct Buffer {
    BufferElement buf[WINDOW_SIZE];
    int startProcessIdx;
    int highestProcessIdx;
    int readyProcessIdx;
    int isAlive;
    int maxPossibleProcessIdx;
};
typedef struct Buffer Buffer;

/* Data Structures */
WindowElement window[WINDOW_SIZE];
Buffer buffer[MAX_NUM_MACHINES];
int highestDeliveredCtr[MAX_NUM_MACHINES];

/* Global Variables */
int numMachines;
int myIP;
int noRecvCtr = 0, machineIdx, msgCtr = 0, startWindowIdx = 0;
int lastDeliveredCtr = -1;
struct sockaddr_in    zeroAddr;
FILE *fd; /* Pointer to dest file, which we write  */
int ss; /* Sending Socket */


void myprintf (__const char *format, ...) {

}

unsigned long bigrandom() {
    int i;
    unsigned long n = 0;
    /* First digit must be non-zero */
    do {
        n = rand() % 10;
    } while (n == 0);
    for (i = 1; i < 7; i++) {
        n *= 10;
        n += rand() % 10;
    }
    return n;
}

int getWindowIdx(int idx) {
    return idx % WINDOW_SIZE;
}

int getBufferIdx(int idx) {
    return idx % WINDOW_SIZE;
}

void printPacket(char* packet) {
    Header *recvHdr= (Header*) packet;
    int i,j, curTotalNacks = 0, printVar;
    myprintf ("[ Type= %d | Pid=%d | PIdx=%d | Ctr=%d | ldCtr=%d | sAddr=%d] \n",
                    recvHdr->packetType, recvHdr->pid, recvHdr->idx, recvHdr->counter, recvHdr->lastDeliveredCtr, recvHdr->senderAddress);
    myprintf ("[ NackCounts = ");
    for(i=0; i<MAX_NUM_MACHINES; i++) {
        myprintf ("nc[%d]=%d, ", i, recvHdr->nackCount[i]);
        curTotalNacks+=recvHdr->nackCount[i];
    }
    myprintf ("]\n[ Nacks = ");
    for(i=0; i<curTotalNacks; i++) {
        printVar = *((int *)(packet + HEADER_SIZE + i*sizeof(int)));
        myprintf ("nl[%d]=[%d], ", i, printVar);
    }
    myprintf ("]\n");
}

struct timeval intializeNackSentTime() {
    struct timeval retTv;
    struct timeval curTime;
    long int curMilliSec;
    long int initMilliSec;
    gettimeofday(&curTime, NULL);

    curMilliSec = (curTime.tv_sec * 1000 + curTime.tv_usec / 1000);
    initMilliSec = curMilliSec - NACK_TIMEOUT;

    retTv.tv_sec = initMilliSec/1000;
    retTv.tv_usec = (initMilliSec % 1000) * 1000;

    return retTv;
}

long int computeDiff(struct timeval tv1, struct timeval tv2) {
    long int milliSecDiff;
    milliSecDiff = (tv1.tv_sec * 1000 + tv1.tv_usec / 1000) - (tv2.tv_sec * 1000 + tv2.tv_usec / 1000);
    return milliSecDiff;
}

struct timeval getCurrentTime() {
    struct timeval curTime;
    gettimeofday(&curTime, NULL);
    return curTime;
}

int testIntializeNackSentTime() {
    struct timeval initTv = intializeNackSentTime();
    struct timeval curTime;
    gettimeofday(&curTime, NULL);
    myprintf ("%d\n" ,computeDiff(curTime, initTv));
}

int min(int *arr, int length) {
    int min = INFINITY, i;
    for (i = 0; i < length; i++) {
        if (arr[i] < min) {
            min = arr[i];
        }
    }
    return min;
}

int haveAllReachedHighest(int *tempIdx) {
    int i=0;
    for(i=0; i<MAX_NUM_MACHINES; i++) {
        if(tempIdx[i] <= buffer[i].highestProcessIdx) {
            return FALSE;
        }
    }
    return TRUE;
}

/* Populate listOfNacks */
int populateListOfNacks(char *packet) {
    int i, j, totalNacks = 0, tempTotalNackBytes = 0, curNackIdx = 0;
    int tempListOfNacks[MAX_NUM_MACHINES][MAX_NACKS];
    int tempNc[MAX_NUM_MACHINES];
    int tempNackList[MAX_NACKS];
    int tempIdx[MAX_NUM_MACHINES];
    Header *sendHdr = (Header*)packet;

    struct timeval curTime;
    gettimeofday(&curTime, NULL);
    
    memset(sendHdr->nackCount, 0, sizeof (sendHdr->nackCount));

    for (i = 0; i < numMachines; i++) {
        tempIdx[i] = buffer[i].startProcessIdx;
        tempNc[i] = 0;
        myprintf ("InPop: SPIdx[%d]: %d  HPIdx[%d]: %d\n", i, buffer[i].startProcessIdx,i, buffer[i].highestProcessIdx);
    }

    while((!haveAllReachedHighest(tempIdx)) && (totalNacks < MAX_NACKS)) {
        for (j = 0; j < numMachines; j++) {
            if( buffer[j].buf[getBufferIdx(tempIdx[j])].isSpaceUsed == FALSE
                    && tempIdx[j] <= buffer[j].highestProcessIdx
                    && computeDiff(curTime, buffer[j].buf[getWindowIdx(tempIdx[j])].lastNackSentTime) > NACK_TIMEOUT ) {
                tempListOfNacks[j][tempNc[j]] = tempIdx[j];
                tempNc[j] = tempNc[j]+1;
                totalNacks++;
            }
        }
        for (j = 0; j < numMachines; j++) {
            tempIdx[j] = tempIdx[j] + 1;
        }
    }

    memcpy(sendHdr->nackCount, tempNc, sizeof(sendHdr->nackCount));
    tempTotalNackBytes=0;
    curNackIdx = 0;
    for(i=0; i<MAX_NUM_MACHINES;i++) {
        for(j=0; j<tempNc[i];j++) {
            myprintf ("Assigning tempNackList[%d]=%d;\n", curNackIdx, tempListOfNacks[i][j]);
            tempNackList[curNackIdx] = tempListOfNacks[i][j];
            curNackIdx++;
        }
    }
    memcpy((packet+HEADER_SIZE), tempNackList, curNackIdx*sizeof(int));

/*
    printf("InPop: sendPkt: ");
    printPacket(packet);
*/

    myprintf ("InPop: temp: ");
    for(i=0; i<MAX_NUM_MACHINES;i++) {
        myprintf ("nc[%d]=%d ", i, sendHdr->nackCount[i]);
    }
    myprintf ("\n InPop: temp: ");
    for(i=0; i<MAX_NUM_MACHINES;i++) {
        for(j=0; j<tempNc[i];j++) {
            myprintf ("tLoN[%d][%d]=%d ", i, j, tempListOfNacks[i][j]);
        }
    }
    myprintf ("\n");
    return tempTotalNackBytes;
}

int isAllBufferDead() {
    int i;
    for (i = 0; i < numMachines; i++) {
        if(buffer[i].isAlive == TRUE) {
            return FALSE;
        }
    }
    return TRUE;
}

void handleRecvPacket(char *recv_mess_buf) {
    Header *curDeliverHdr;
    Header *recvHdr;
    int i, j, tempTotalNacks, tempReadyProcessIdx, maxDeliverableCtr, tempWindowIdx, tempMinCtr, isDeliver, tempMinStartProcessIdxCtr;
    int tempProcessId, processIdxToBeResent;
    struct sockaddr_in    unicastSenderAddr;

    recvHdr=(Header*)recv_mess_buf;

    if(recvHdr->pid != machineIdx && recvHdr->packetType==TYPE_MCAST_PACKET ) {
        /* Recieved something valid data packet from others */
        noRecvCtr = 0;
        /* TODO: Only recieve others packets. Ignore self ones. */
    }

    if(recvHdr->idx < buffer[recvHdr->pid].startProcessIdx && recvHdr->packetType==TYPE_MCAST_PACKET ) {
        /* Discard the packet */
        return;
    }

    /* Update the counter to the highest seen */
    if(recvHdr->counter!=INFINITY && recvHdr->counter > msgCtr){
        msgCtr=recvHdr->counter;
    }

    if(recvHdr->packetType==TYPE_MCAST_PACKET){
        /* Copy packet to the buffer */
        memcpy(&(buffer[recvHdr->pid].buf[getBufferIdx(recvHdr->idx)].packetHdr), recv_mess_buf, HEADER_SIZE);
        buffer[recvHdr->pid].buf[getBufferIdx(recvHdr->idx)].isSpaceUsed = TRUE;
    }

    if(recvHdr->packetType==TYPE_MCAST_PACKET){
        /* Recalculating highestProcessIdx */
        if(recvHdr->idx > buffer[recvHdr->pid].highestProcessIdx) {
            buffer[recvHdr->pid].highestProcessIdx = recvHdr->idx;
        }

        myprintf ("Before: SPIdx %d %d %d ReadyProcessIdx %d %d %d\n", buffer[0].startProcessIdx, buffer[1].startProcessIdx, buffer[2].startProcessIdx, buffer[0].readyProcessIdx, buffer[1].readyProcessIdx, buffer[2].readyProcessIdx);
        /* Recalculating readyProcessIdx */
        for(tempReadyProcessIdx=buffer[recvHdr->pid].readyProcessIdx+1; tempReadyProcessIdx <= buffer[recvHdr->pid].highestProcessIdx; tempReadyProcessIdx++){
            if(buffer[recvHdr->pid].buf[getBufferIdx(tempReadyProcessIdx)].isSpaceUsed == FALSE ){
                break;
            } else {
                buffer[recvHdr->pid].readyProcessIdx = tempReadyProcessIdx;
            }
        }
        myprintf ("After: SPIdx %d %d %d ReadyProcessIdx %d %d %d\n", buffer[0].startProcessIdx, buffer[1].startProcessIdx, buffer[2].startProcessIdx, buffer[0].readyProcessIdx, buffer[1].readyProcessIdx, buffer[2].readyProcessIdx);
    }

    if(recvHdr->packetType==TYPE_MCAST_PACKET
            || recvHdr->packetType==TYPE_WINDOW_BLOCKED
            || recvHdr->packetType==TYPE_PACKET_EXHAUSTED){
        /* Populate last delivered ctr array element */
        if (highestDeliveredCtr[recvHdr->pid] < recvHdr->lastDeliveredCtr){
            highestDeliveredCtr[recvHdr->pid] = recvHdr->lastDeliveredCtr;
        }
        /* Slide sender window */
        myprintf ("1: StartWindowIdx : %d %d \n", startWindowIdx, maxDeliverableCtr);
        maxDeliverableCtr=min(highestDeliveredCtr, numMachines);
        myprintf ("2: StartWindowIdx : %d %d \n", startWindowIdx, maxDeliverableCtr);
        for(tempWindowIdx=startWindowIdx;
                ((Header*)window[getWindowIdx(tempWindowIdx)].sendPkt)->counter <= maxDeliverableCtr; tempWindowIdx++) {
            window[getWindowIdx(tempWindowIdx)].isSpaceUsed = FALSE;
            ((Header*)(window[getWindowIdx(tempWindowIdx)].sendPkt))->counter = INFINITY;
        }
        startWindowIdx = tempWindowIdx;
        myprintf ("3: StartWindowIdx : %d %d \n", startWindowIdx, maxDeliverableCtr);

    }


    if(recvHdr->packetType==TYPE_MCAST_PACKET
            || recvHdr->packetType==TYPE_PACKET_EXHAUSTED){
        /* Deliver packets */
        /* Note: Try to deliver even when PACKET_EXHAUSTED is received. Corner case! */

        /* 1. Get minimum readyIdx counter */
        tempMinCtr = INFINITY;
        isDeliver = TRUE;
        for(i = 0; i < numMachines ;i++) {
            if(buffer[i].isAlive==TRUE){
                if (buffer[i].readyProcessIdx==-1
                        || buffer[i].buf[getBufferIdx(buffer[i].readyProcessIdx)].packetHdr.counter == INFINITY
                        || buffer[i].buf[getBufferIdx(buffer[i].startProcessIdx)].isSpaceUsed == FALSE /* This to cover the corner case.*/ ){
                    isDeliver = FALSE;
                    myprintf ("Do not deliver because (i=%d) %d || %d || %d \n", i, (buffer[i].readyProcessIdx == -1),
                                        (buffer[i].buf[getBufferIdx(buffer[i].readyProcessIdx)].packetHdr.counter == INFINITY),
                                        (buffer[i].buf[getBufferIdx(buffer[i].startProcessIdx)].isSpaceUsed == FALSE));
                    break;
                } else {
                    myprintf ("Before: tempMinCtr = %d Counter[%d]=%d \n", tempMinCtr, i, buffer[i].buf[getBufferIdx(buffer[i].readyProcessIdx)].packetHdr.counter);
                    if(buffer[i].buf[getBufferIdx(buffer[i].readyProcessIdx)].packetHdr.counter < tempMinCtr){
                        tempMinCtr=buffer[i].buf[getBufferIdx(buffer[i].readyProcessIdx)].packetHdr.counter;
                    }
                    myprintf ("After: tempMinCtr = %d\n", tempMinCtr);
                }
            }
        }

        if(isDeliver == TRUE) {
            /* 2. Deliver till tempMinCtr */

            /* Find the minimum counter */
            tempProcessId = -1;
            tempMinStartProcessIdxCtr = INFINITY;
            for(i = 0; i < numMachines ;i++) {
                if(buffer[i].isAlive == TRUE
                        && buffer[i].buf[getBufferIdx(buffer[i].startProcessIdx)].packetHdr.counter < tempMinStartProcessIdxCtr) {
                    tempMinStartProcessIdxCtr = buffer[i].buf[getBufferIdx(buffer[i].startProcessIdx)].packetHdr.counter;
                    tempProcessId = i;
                }
            }
            myprintf ("In while %d %d %d\n", tempMinStartProcessIdxCtr, tempProcessId, tempMinCtr);
            while (tempMinCtr != INFINITY && tempMinStartProcessIdxCtr <= tempMinCtr) {
                if(tempProcessId != -1) {
                    /* Deliver the packet */
                    curDeliverHdr = &(buffer[tempProcessId].buf[getBufferIdx(buffer[tempProcessId].startProcessIdx)].packetHdr);
                    myprintf (" Writing %5d, %6d, %6d, %6d\n", curDeliverHdr->pid, curDeliverHdr->idx, curDeliverHdr->counter, curDeliverHdr->randomData);
                    fprintf(fd, "%5d, %6d, %6d\n", curDeliverHdr->pid, curDeliverHdr->idx, curDeliverHdr->randomData);

                    /* Note the lastDelivered counter */
                    if(lastDeliveredCtr < curDeliverHdr->counter) {
                        lastDeliveredCtr = curDeliverHdr->counter;
                    }

                    /* Free the space */
                    buffer[tempProcessId].buf[getBufferIdx(buffer[tempProcessId].startProcessIdx)].isSpaceUsed = FALSE;
                    buffer[tempProcessId].buf[getBufferIdx(buffer[tempProcessId].startProcessIdx)].lastNackSentTime = intializeNackSentTime();
                    curDeliverHdr->counter = INFINITY;


                    /* Mark the corresponding process buffer as DEAD if the maxPossibleProcessIdx of that process is delivered */
                    /* This is because, it means that, all packets are of that process are delivered. */
                    if(buffer[tempProcessId].maxPossibleProcessIdx == curDeliverHdr->idx) {
                        myprintf ("Killing the buffer[%d] (coz maximum was delivered)\n", tempProcessId);
                        buffer[tempProcessId].isAlive = FALSE;
                    }

                    /* Move the start idx */
                    buffer[tempProcessId].startProcessIdx++;

                }
                /* Find the minimum counter */
                tempMinStartProcessIdxCtr = INFINITY;
                for(i = 0; i < numMachines ;i++) {
                    if(buffer[i].isAlive == TRUE
                            && buffer[i].buf[getBufferIdx(buffer[i].startProcessIdx)].packetHdr.counter < tempMinStartProcessIdxCtr) {
                        tempMinStartProcessIdxCtr = buffer[i].buf[getBufferIdx(buffer[i].startProcessIdx)].packetHdr.counter;
                        tempProcessId = i;
                    }
                }
            }
        }
    }

    if(recvHdr->packetType==TYPE_MCAST_PACKET
            || recvHdr->packetType==TYPE_WINDOW_BLOCKED
            || recvHdr->packetType==TYPE_PACKET_EXHAUSTED){
        /* Respond for the list of nacks */
        unicastSenderAddr = zeroAddr;
        unicastSenderAddr.sin_addr.s_addr = recvHdr->senderAddress;
        tempTotalNacks = 0;
        for (i = 0; i < machineIdx; i++) {
            tempTotalNacks += recvHdr->nackCount[i];
        }
        for (i = 0; i < recvHdr->nackCount[machineIdx]; i++) {
            processIdxToBeResent = *((int *)(recv_mess_buf + HEADER_SIZE + (tempTotalNacks + i)*sizeof(int)));
            memset(((Header *)(window[getWindowIdx(processIdxToBeResent)].sendPkt))->nackCount, 0, MAX_NUM_MACHINES*sizeof(int));
            /* Send only if the packet requested falls into the current window. */
            if(processIdxToBeResent < startWindowIdx) {
                /* Do not resend */
                myprintf ("--> Not Resending packet with processIdx : %d\n", processIdxToBeResent);
            } else {
                myprintf ("--> Resending to [%d]: ", unicastSenderAddr.sin_addr.s_addr);
                printPacket(window[getWindowIdx(processIdxToBeResent)].sendPkt);
                sendto(ss, window[getWindowIdx(processIdxToBeResent)].sendPkt ,
                                    PACKET_SIZE , 0, (struct sockaddr *)&unicastSenderAddr, sizeof(unicastSenderAddr) );
            }
        }
    }

    if(recvHdr->packetType==TYPE_PACKET_EXHAUSTED
            || recvHdr->packetType==TYPE_WINDOW_BLOCKED ){
        /* Assign last process index to highest process index */
        buffer[recvHdr->pid].highestProcessIdx = recvHdr->idx;
    }

    if(recvHdr->packetType==TYPE_PACKET_EXHAUSTED){
        /* Assign maxPossibleProcessIdx for that process */
        buffer[recvHdr->pid].maxPossibleProcessIdx = recvHdr->idx;
        /* Remove this from Buffer array if all elements are delivered */
        if(buffer[recvHdr->pid].startProcessIdx > buffer[recvHdr->pid].highestProcessIdx) {
            myprintf ("Killing the buffer[%d] (coz all delivered)\n", recvHdr->pid);
            buffer[recvHdr->pid].isAlive = FALSE;
        }
    }
}

int main(int argc, char **argv) {
    struct sockaddr_in name;
    struct sockaddr_in mcastAddr;
    struct timeval startTime;

    int mcast_addr;

    struct ip_mreq mreq;
    unsigned char ttl_val;

    int sr;
    fd_set mask;
    fd_set dummy_mask, temp_mask;
    int bytes;
    int num;
    char recv_mess_buf[PACKET_SIZE];
    char recv_mess_buf1[PACKET_SIZE];
    char send_mess_buf[PACKET_SIZE];
    struct timeval timeout;

    Header *recvHdr = (Header *) recv_mess_buf;

    int curWindowIdx = 0, pid;
    int totalNumPktsToSend, lossRate;
    int numPktsSent = INFINITY; /* Just to enter into the for loop initially */
    Header *sendHdr;
    int i, j;
    int totalSent;
    char filePath[NAME_LENGTH];

    if (argc != 5) {
        printf("Usage: mcast <number of packets> <machine index> <number of machines> <loss_rate>\n");
        exit(1);
    }

    totalNumPktsToSend = atoi(argv[1]);
    machineIdx = atoi(argv[2]);
    numMachines = atoi(argv[3]);
    lossRate = atoi(argv[4]);
    strncpy(filePath, argv[2], 1); /* ProcessIdx is only 1 byte long(0 to 9) */
    strncat(filePath, ".out", sizeof(".out"));

    if (numMachines > MAX_NUM_MACHINES) {
        printf("Number of machines should not exceed %d \n", MAX_NUM_MACHINES);
        exit(1);
    }

    mcast_addr = MCAST_IP;

    sr = socket(AF_INET, SOCK_DGRAM, 0); /* socket for receiving */
    if (sr < 0) {
        perror("Mcast: socket");
        exit(1);
    }

    name.sin_family = AF_INET;
    name.sin_addr.s_addr = INADDR_ANY;
    name.sin_port = htons(PORT);

    if (bind(sr, (struct sockaddr *) & name, sizeof (name)) < 0) {
        perror("Mcast: bind");
        exit(1);
    }

    mreq.imr_multiaddr.s_addr = htonl(mcast_addr);

    /* the interface could be changed to a specific interface if needed */
    mreq.imr_interface.s_addr = htonl(INADDR_ANY);

    if (setsockopt(sr, IPPROTO_IP, IP_ADD_MEMBERSHIP, (void *) & mreq,
            sizeof (mreq)) < 0) {
        perror("Mcast: problem in setsockopt to join multicast address");
    }

    ss = socket(AF_INET, SOCK_DGRAM, 0); /* Socket for sending */
    if (ss < 0) {
        perror("Mcast: socket");
        exit(1);
    }

    ttl_val = 1;
    if (setsockopt(ss, IPPROTO_IP, IP_MULTICAST_TTL, (void *) & ttl_val,
            sizeof (ttl_val)) < 0) {
        printf("Mcast: problem in setsockopt of multicast ttl %d - ignore in WinNT or Win95\n", ttl_val);
    }

    mcastAddr.sin_family = AF_INET;
    mcastAddr.sin_addr.s_addr = htonl(mcast_addr); /* mcast address */
    mcastAddr.sin_port = htons(PORT);

    myIP=getMyIP();
    for (i = 0; i < MAX_NUM_MACHINES; i++) {
        buffer[i].startProcessIdx = 0;
        buffer[i].readyProcessIdx = -1;
        buffer[i].highestProcessIdx = -1;
        buffer[i].isAlive = TRUE;
        buffer[i].maxPossibleProcessIdx = INFINITY;
        for(j = 0; j < WINDOW_SIZE; j++) {
            buffer[i].buf[j].packetHdr.counter = INFINITY;
            buffer[i].buf[j].isSpaceUsed = FALSE;
            buffer[i].buf[j].lastNackSentTime = intializeNackSentTime();
        }
        highestDeliveredCtr[i] = -1;
    }
    for (i = 0; i < WINDOW_SIZE; i++) {
        window[i].isSpaceUsed = FALSE;
        ((Header*)(window[i].sendPkt))->counter = INFINITY;
        ((Header*)(window[i].sendPkt))->senderAddress = myIP;
    }
    if((fd = fopen(filePath, "w")) == NULL){
        perror("fopen");
        exit(1);
    }

    zeroAddr.sin_family = AF_INET;
    zeroAddr.sin_addr.s_addr = 0;
    zeroAddr.sin_port = htons(PORT);

    recv_dbg_init(lossRate, machineIdx);

    FD_ZERO(&mask);
    FD_ZERO(&dummy_mask);
    FD_SET(sr, &mask);

    for (;;) {
        temp_mask = mask;
        timeout.tv_sec = 10;
        timeout.tv_usec = 0;
        num = select(FD_SETSIZE, &temp_mask, &dummy_mask, &dummy_mask, &timeout);
        if (num > 0) {
            if (FD_ISSET(sr, &temp_mask)) {
                bytes = recv(sr, recv_mess_buf1, sizeof (recv_mess_buf1), 0);
                myprintf ("Something recieved\n");
                myprintf ("<-- ");
                printPacket(recv_mess_buf1);
                if (((Header*)(recv_mess_buf1))->packetType == TYPE_START_MCAST) {
                    printf("START_MCAST packet received \n");
                    break; /* Starts multicasting the packets below */
                }
            }
        } else {
            printf(".");
            fflush(0);
        }
    }

    startTime = getCurrentTime();
    
    for (; !(isAllBufferDead() && curWindowIdx == startWindowIdx);) /* Do until nothing is received from "others" */ {
        myprintf ("In Outer loop\n");

        numPktsSent = 0;
        while (curWindowIdx < totalNumPktsToSend && numPktsSent < SEND_BURST_SIZE && (curWindowIdx - startWindowIdx) < WINDOW_SIZE) {
            sendHdr = (Header*) (window[getWindowIdx(curWindowIdx)].sendPkt);
            sendHdr->packetType = TYPE_MCAST_PACKET;
            sendHdr->counter = ++msgCtr;
            sendHdr->pid = machineIdx;
            sendHdr->idx = curWindowIdx;
            sendHdr->randomData = bigrandom(); /* TODO: Generate random number */
            sendHdr->lastDeliveredCtr = lastDeliveredCtr;

            /* Populate listOfNacks */
            populateListOfNacks(window[getWindowIdx(curWindowIdx)].sendPkt);

            myprintf ("--> ");
            printPacket(window[getWindowIdx(curWindowIdx)].sendPkt);
            sendto( ss, window[getWindowIdx(curWindowIdx)].sendPkt,PACKET_SIZE , 0,(struct sockaddr *)&mcastAddr, sizeof(mcastAddr) );
            handleRecvPacket(window[getWindowIdx(curWindowIdx)].sendPkt); /* Handle the packet as if it was recieved. */

            numPktsSent++;
            curWindowIdx++;
        }
        if(numPktsSent == 0) {
            sendHdr = (Header*)send_mess_buf;
            sendHdr->pid = machineIdx;
            sendHdr->lastDeliveredCtr = lastDeliveredCtr;
            sendHdr->senderAddress = myIP;
            /* Populate listOfNacks */
            populateListOfNacks(send_mess_buf);

            /* Window block case */
            if((curWindowIdx - startWindowIdx) == WINDOW_SIZE) {
                sendHdr->packetType = TYPE_WINDOW_BLOCKED;
                sendHdr->idx = curWindowIdx-1; /* Process Index of the last data packet sent */
                myprintf ("--> ");
                printPacket(send_mess_buf);
                sendto( ss, send_mess_buf,PACKET_SIZE , 0,(struct sockaddr *)&mcastAddr, sizeof(mcastAddr) );
                handleRecvPacket(send_mess_buf); /* Handle the packet as if it was recieved. */
            } 

            /* Packets exhausted case */
            if(curWindowIdx == totalNumPktsToSend) {
                sendHdr->packetType = TYPE_PACKET_EXHAUSTED;
                sendHdr->idx = curWindowIdx-1; /* Process Index of the last data packet sent */
                myprintf ("--> ");
                printPacket(send_mess_buf);
                sendto( ss, send_mess_buf,PACKET_SIZE , 0,(struct sockaddr *)&mcastAddr, sizeof(mcastAddr) );
                handleRecvPacket(send_mess_buf); /* Handle the packet as if it was recieved. */
            }
        }

        for (totalSent = 0; totalSent < RECV_BURST_SIZE;) {
            myprintf ("In receive\n");
            timeout.tv_sec = RECV_WAIT_TIME_SEC;
            timeout.tv_usec = RECV_WAIT_TIME_MICROSEC;
            temp_mask = mask;
            num = select(FD_SETSIZE, &temp_mask, &dummy_mask, &dummy_mask, &timeout);
            if (num > 0) {
                if (FD_ISSET(sr, &temp_mask)) {
                    totalSent++;
                    bytes = recv_dbg(sr, recv_mess_buf, sizeof (recv_mess_buf), 0);
                    if(bytes==0){
                        myprintf ("nothing received\n");
                    }else{
                        myprintf ("<-- ");
                        printPacket(recv_mess_buf);
                        recvHdr=(Header*)recv_mess_buf;
                        if(recvHdr->pid != machineIdx) {
                            /* Handle only packets of others. Drop self packets because this already handled during sending */
                            handleRecvPacket(recv_mess_buf);
                        }
                    }
                }
            } else {
                noRecvCtr++;
                myprintf ("Breaking because nothing recieved for [%d] times\n", noRecvCtr);
                break;
            }
        }
    }

    /* Finish: Send to everyone that I recieved everything and die. */

    for(i = 0; i < 10; i++) {
        sendHdr = (Header*)send_mess_buf;
        sendHdr->pid = machineIdx;
        sendHdr->lastDeliveredCtr = lastDeliveredCtr;
        sendHdr->packetType = TYPE_PACKET_EXHAUSTED;
        sendHdr->idx = curWindowIdx-1; /* Process Index of the last data packet sent */
        myprintf ("--> SENDING FINAL PACKET <--");
        printPacket(send_mess_buf);
        sendto( ss, send_mess_buf,PACKET_SIZE , 0,(struct sockaddr *)&mcastAddr, sizeof(mcastAddr) );
    }

    fclose(fd);

    printf("Total time taken = %dms\n", computeDiff(getCurrentTime(), startTime));
    return 0;
}